package com.one.rope.mvp.basecore.util;

import java.util.Random;


public class DigitIdGenerator {

  public static final int NODE_SHIFT = 6;
  public static final int SEQ_SHIFT = 6;
  public static final int RANDOM_SHIFT = 10;


  public static final int MAX_NODE = 1 << NODE_SHIFT;
  public static final int MAX_SEQUENCE = 1 << SEQ_SHIFT;
  public static final int MAX_SEQUENCE_INDEX = MAX_SEQUENCE - 1;
  public static final int MAX_RANDOM = 1 << RANDOM_SHIFT;

  private short sequence;
  private long referenceTime;

  private int node;
  private long random;

  /**
   * A snowflake is designed to operate as a singleton instance within the context of a node. If you
   * deploy different nodes, supplying a unique node id will guarantee the uniqueness of ids
   * generated concurrently on different nodes.
   *
   * @param node This is an id you use to differentiate different nodes.
   */

  public DigitIdGenerator(int node) {
    if (node < 0) {
      node = 0 - node;
    }
    if (node > MAX_NODE) {
      // throw new IllegalArgumentException(String.format("node must be
      // between %s and %s", 0, MAX_NODE));
      node = node % MAX_NODE;
    }
    this.node = node;

    setRandomValue(0);
  }


  /**
   * Generates a k-ordered unique 64-bit integer. Subsequent invocations of this method will produce
   * increasing integer values.
   *
   * @return The next 64-bit integer.
   */
  public long next() {
    long currentTime = 0;
    long counter;
    synchronized (this) {
      currentTime = System.currentTimeMillis();
      if (currentTime < referenceTime) {//时间倒流异常
        throw new RuntimeException(
            String.format("Last referenceTime %s is after reference time %s", referenceTime,
                currentTime));
      } else if (currentTime > referenceTime) {//时间不是同一毫秒
        long oldSequence = sequence;
        this.sequence = 0;
        //System.out.println("currentTime="+currentTime+",oldSequence="+oldSequence+";sequence="+sequence);
      } else {
        if (this.sequence < DigitIdGenerator.MAX_SEQUENCE_INDEX) {//当前毫秒的id生成数量超过可以生成的最大数量
          //long oldSequence=sequence;
          this.sequence++;
          //System.out.println("currentTime="+currentTime+",oldSequence="+oldSequence+";sequence="+sequence);
        } else {
          try {//让CPU休息一毫秒
            Thread.sleep(1);
          } catch (Exception e) {
            throw new RuntimeException("ID生成器异常,超出生成范围");
          } finally {
            long oldCurrentTime = currentTime;
            currentTime = System.currentTimeMillis();
            if (oldCurrentTime == currentTime) {
              throw new RuntimeException("时间休眠异常");
            }
            this.sequence = 0;
          }
          //throw new RuntimeException("Sequence exhausted at " + this.sequence);
        }
      }
      counter = this.sequence;
      referenceTime = currentTime;
    }

    return currentTime << NODE_SHIFT << SEQ_SHIFT << RANDOM_SHIFT
        | node << SEQ_SHIFT << RANDOM_SHIFT
        | counter << RANDOM_SHIFT
        | random
        ;
  }

  public long next(long random) {
    setRandomValue(random);
    return next();
  }

  private void setRandomValue(long random) {
    if (random == 0) {
      random = new Random().nextInt(1024124) % MAX_RANDOM;
    }
    if (random < 0) {
      random = 0 - random;
    }
    if (random > MAX_RANDOM) {
      random = random % MAX_RANDOM;
    }
    this.random = random;
  }

}
